package com.hex.ds.hdrs.cron.service;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.bigdata.hbda.base.Page;
import com.hex.bigdata.hbda.utils.FileUtil;
import com.hex.ds.hdrs.agent.po.AgentConf;
import com.hex.ds.hdrs.agent.service.AgentConfService;
import com.hex.ds.hdrs.common.annotation.AppCodeVerification;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.enums.CronTaskDefStatusEnum;
import com.hex.ds.hdrs.common.enums.ExcelImportOperationEnum;
import com.hex.ds.hdrs.common.excel.CronTaskImportVerifyHandler;
import com.hex.ds.hdrs.common.page.PageData;
import com.hex.ds.hdrs.common.utils.CheckFileUtil;
import com.hex.ds.hdrs.common.utils.ExcelDownUtil;
import com.hex.ds.hdrs.common.utils.MultipartFileUtil;
import com.hex.ds.hdrs.cron.converter.ICronTaskDefConverter;
import com.hex.ds.hdrs.cron.dao.CronTaskDefDao;
import com.hex.ds.hdrs.cron.dto.CronTaskDefDto;
import com.hex.ds.hdrs.cron.dto.CronTaskImportDto;
import com.hex.ds.hdrs.cron.po.CronTaskDef;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Package: com.hex.ds.hdrs.cron.service
 * @ClassName CronTaskDefService
 * @Description: 定时任务定义业务服务实现
 * @Author: wz.li
 * @Date 2023/4/4 16:14
 * @Version v2.0.2
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class CronTaskDefService {

    private final CronTaskDefDao cronTaskDefDao;

    private final CronTaskImportVerifyHandler cronTaskImportVerifyHandler;

    private final ICronTaskDefConverter converter;

    private final CronTaskService cronTaskService;

    private final CronService cronService;

    private final AgentConfService agentConfService;

    @Value("${file.path}")
    private String filePath;

    /*
     * @Method: queryListByPage <br>
     * @Param: [cronTaskDefDto, page] <br>
     * @Return: com.hex.ds.hdrs.common.page.PageData <br>
     * @Description：分页查询<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 16:38 <br>
     * @Version： V2.0.2<br>
     */
    public PageData queryListByPage(CronTaskDefDto cronTaskDefDto, Page page) {
        List<CronTaskDef> cronTaskDefList = cronTaskDefDao.queryListByPage(converter.toCronTaskDefQuery(cronTaskDefDto), page);
        for (CronTaskDef cronTaskDef : cronTaskDefList) {
            if (StrUtil.isNotBlank(cronTaskDef.getAppointAgentCode())){
                AgentConf agentConf = agentConfService.qryAgentConfByAgentCode(cronTaskDef.getAppointAgentCode());
                if (agentConf != null){
                    cronTaskDef.setAppointAgentName(agentConf.getAgentName());
                }
            }
        }
        return new PageData(cronTaskDefList, page);
    }

    /*
     * @Method: queryListByAppCode <br>
     * @Param: [appCode] <br>
     * @Return: java.util.List<com.hex.ds.hdrs.cron.po.CronTaskDef> <br>
     * @Description：查询指定应用下的定时任务定义集合<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 15:00 <br>
     * @Version： V2.0.2<br>
     */
    public List<CronTaskDef> queryListByAppCode(String appCode) {
        return queryList(new CronTaskDefDto().setAppCode(appCode));
    }

    /*
     * @Method: queryList <br>
     * @Param: [cronTaskDefDto] <br>
     * @Return: java.util.List<com.hex.ds.hdrs.cron.po.CronTaskDef> <br>
     * @Description：根据指定条件查询定时任务定义集合<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 10:24 <br>
     * @Version： V2.0.2<br>
     */
    public List<CronTaskDef> queryList(CronTaskDefDto cronTaskDefDto){
        return cronTaskDefDao.queryList(converter.toCronTaskDefQuery(cronTaskDefDto));
    }

    /*
     * @Method: queryByTaskCode <br>
     * @Param: [taskCode] <br>
     * @Return: com.hex.ds.hdrs.cron.po.CronTaskDef <br>
     * @Description：查询指定任务编码对应的定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 14:56 <br>
     * @Version： V2.0.2<br>
     */
    public CronTaskDef queryByTaskCode(String taskCode) {
        return queryOne(new CronTaskDefDto().setTaskCode(taskCode));
    }

    /*
     * @Method: queryByPkId <br>
     * @Param: [pkId] <br>
     * @Return: com.hex.ds.hdrs.cron.po.CronTaskDef <br>
     * @Description：查询指定主键对应的定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 14:56 <br>
     * @Version： V2.0.2<br>
     */
    public CronTaskDef queryByPkId(String pkId) {
        return queryOne(new CronTaskDefDto().setPkId(pkId));
    }

    /*
     * @Method: queryOne <br>
     * @Param: [cronTaskDefDto] <br>
     * @Return: com.hex.ds.hdrs.cron.po.CronTaskDef <br>
     * @Description：根据指定条件查询单个定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 10:37 <br>
     * @Version： V2.0.2<br>
     */
    public CronTaskDef queryOne(CronTaskDefDto cronTaskDefDto) {
        return cronTaskDefDao.queryOne(converter.toCronTaskDefQuery(cronTaskDefDto));
    }

    @AppCodeVerification(paramObject = CronTaskDefDto.class)
    public void add(CronTaskDefDto cronTaskDefDto) {
        int count = cronTaskDefDao.insert(converter.toCronTaskDefForAdd(cronTaskDefDto));
        if (count != 1){
            throw new RuntimeException("定时任务定义新增失败");
        }
    }

    /*
     * @Method: modifyById <br>
     * @Param: [cronTaskDef] <br>
     * @Return: void <br>
     * @Description：定时任务定义更新<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 16:31 <br>
     * @Version： V2.0.2<br>
     */
    @AppCodeVerification(paramObject = CronTaskDefDto.class)
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyById(CronTaskDefDto cronTaskDefDto) {
        modifyDbDataById(converter.toCronTaskDefForUpdate(cronTaskDefDto));
        updateScheduledFuture(cronTaskDefDto.getPkId());
    }

    /*
     * @Method: modifyDbDataById <br>
     * @Param: [cronTaskDefDto] <br>
     * @Return: void <br>
     * @Description：更新定时任务定义数据库数据<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 14:10 <br>
     * @Version： V2.0.2<br>
     */
    private void modifyDbDataById(CronTaskDef cronTaskDef){
        int count = cronTaskDefDao.update(cronTaskDef);
        if (count != 1){
            throw new RuntimeException("定时任务定义更新失败");
        }
    }

    /*
     * @Method: updateScheduledFuture <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：更新定时任务的定时器<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 14:10 <br>
     * @Version： V2.0.2<br>
     */
    private void updateScheduledFuture(String pkId){
        CronTaskDef cronTaskDef = queryByPkId(pkId);
        if (cronTaskDef != null && CronTaskDefStatusEnum.ENABLE.equals(cronTaskDef.getTaskStatus())) {
            cronService.stop(cronTaskDef);
            cronService.add(cronTaskDef);
        }
    }

    /*
     * @Method: batchEnableById <br>
     * @Param: [pkIds] <br>
     * @Return: void <br>
     * @Description：批量启用定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 13:45 <br>
     * @Version： V2.0.2<br>
     */
    public void batchEnableById(List<String> pkIds){
        for (String pkId : pkIds) {
            enableCronTask(pkId);
        }
    }

    /*
     * @Method: enableCronTask <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：启用定时任务(非启用状态下才可以启用)<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 14:24 <br>
     * @Version： V2.0.2<br>
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void enableCronTask(String pkId){
        CronTaskDef cronTaskDef = queryByPkId(pkId);
        if (!CronTaskDefStatusEnum.ENABLE.getName().equals(cronTaskDef.getTaskStatus())){
            cronTaskDef.setTaskStatus(CronTaskDefStatusEnum.ENABLE.getName());
            modifyDbDataById(cronTaskDef);
            cronService.add(cronTaskDef);
        }
    }

    /*
     * @Method: batchDisableById <br>
     * @Param: [pkIds] <br>
     * @Return: void <br>
     * @Description：批量停用定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 13:45 <br>
     * @Version： V2.0.2<br>
     */
    public void batchDisableById(List<String> pkIds){
        for (String pkId : pkIds) {
            disableCronTask(pkId);
        }
    }

    /*
     * @Method: disableCronTask <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：停用定时任务<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 14:26 <br>
     * @Version： V2.0.2<br>
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disableCronTask(String pkId){
        CronTaskDef cronTaskDef = queryByPkId(pkId);
        if (!CronTaskDefStatusEnum.DISABLE.getName().equals(cronTaskDef.getTaskStatus())){
            cronTaskDef.setTaskStatus(CronTaskDefStatusEnum.DISABLE.getName());
            modifyDbDataById(cronTaskDef);
            cronService.stop(cronTaskDef);
        }
    }

    /*
     * @Method: batchRemoveById <br>
     * @Param: [pkIds] <br>
     * @Return: void <br>
     * @Description：批量删除定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 16:55 <br>
     * @Version： V2.0.2<br>
     */
    public void batchRemoveById(List<String> pkIds){
        for (String pkId : pkIds){
            removeById(pkId);
        }
    }

   /*
    * @Method: removeById <br>
    * @Param: [pkId] <br>
    * @Return: void <br>
    * @Description：根据主键删除定时任务定义<br>
    * @Author： wz.li<br>
    * @Date： 2023/4/3 16:31 <br>
    * @Version： V2.0.2<br>
    */
   @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void removeById(String pkId) {
        CronTaskDef cronTaskDef = queryByPkId(pkId);
        if (cronTaskDef != null){
            removeDbDataByPkId(pkId);
            cronTaskService.deleteByCode(cronTaskDef.getTaskCode());
            cronService.stop(cronTaskDef);
        }
    }

    /*
     * @Method: removeDbDataByPkId <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：根据主键删除定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 14:18 <br>
     * @Version： V2.0.2<br>
     */
    private void removeDbDataByPkId(String pkId){
        int count = cronTaskDefDao.deleteById(pkId);
        if (count != 1){
            throw new RuntimeException("删除定时任务定义失败");
        }
    }

    /*
     * @Method: importExcel <br>
     * @Param: [multipartFile, batchTag, appCode] <br>
     * @Return: java.util.Map <br>
     * @Description：Excel导入数据<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 16:39 <br>
     * @Version： V2.0.2<br>
     */
    public Map importExcel(MultipartFile multipartFile, String batchTag, String appCode) {
        Map resultMap = new HashMap();
        try {
            // 1.检查导入的文件
            checkFile(multipartFile);
            // 2.备份导入的EXCEL文件
            String backUpFilePath = backupUploadFile(multipartFile, batchTag);
            // 3.读取EXCEL文件数据，并做校验
            List<CronTaskImportDto> excelData = getExcelData(appCode, backUpFilePath);
            // 4.保存数据
            updateDbDataByExcel(excelData, batchTag, appCode);
            resultMap.put("status", true);
        } catch (Exception e) {
            resultMap.put("status", false);
            resultMap.put("message", e.getMessage());
        }
        return resultMap;
    }

    /*
     * @Method: checkFile <br>
     * @Param: [mf] <br>
     * @Return: void <br>
     * @Description：检查上传的文件格式<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 13:32 <br>
     * @Version： V2.0.2<br>
     */
    private void checkFile(MultipartFile mf) {
        if (mf.isEmpty()) {
            throw new RuntimeException("导入的文件为空文件");
        }
        String type = CheckFileUtil.FileTypeUtils.getFileByFile(MultipartFileUtil.multipartFileToFile(mf));
        if (!Const.FILE_XLS.equals(type) && !Const.FILE_XLSX.equals(type)) {
            throw new RuntimeException("文件类型不符合要求！");
        }
    }

    /*
     * @Method: backupUploadFile <br>
     * @Param: [multipartFile, batchTag] <br>
     * @Return: java.lang.String <br>
     * @Description 备份上传的文件<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 13:33 <br>
     * @Version： V2.0.2<br>
     */
    private String backupUploadFile(MultipartFile multipartFile, String batchTag) {
        String backUpPath = FilenameUtils.separatorsToSystem(filePath + File.separator + "static/uploadFile/cronTask");
        com.hex.bigdata.hbda.utils.FileUtil.mkdir(backUpPath);
        String updateFilePath = FilenameUtils.separatorsToSystem(backUpPath + File.separator + batchTag + "_");
        File file = FileUtil.upload(multipartFile, updateFilePath);
        log.debug("上传到服务器的EXCEL文件路径: {}", file.getPath());
        return file.getPath();
    }

    /*
     * @Method: getExcelData <br>
     * @Param: [appCode, backUpFilePath] <br>
     * @Return: java.util.List<com.hex.ds.hdrs.cron.dto.CronTaskImportDto> <br>
     * @Description：读取Excel中的数据<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 13:34 <br>
     * @Version： V2.0.2<br>
     */
    private List<CronTaskImportDto> getExcelData(String appCode, String backUpFilePath) {
        ExcelImportResult<CronTaskImportDto> convertAfterList = readExcel(appCode, backUpFilePath);
        if (CollectionUtil.isNotEmpty(convertAfterList.getFailList())) {
            StringBuffer sb = new StringBuffer();
            for (CronTaskImportDto p : convertAfterList.getFailList()) {
                sb.append("第" + (p.getRowNum() + 1) + "行的错误是：\n" + p.getErrorMsg().replaceAll(",", "\n")).append("\n");
            }
            throw new RuntimeException(sb.toString());
        }
        return convertAfterList.getList();
    }

    /*
     * @Method: readExcel <br>
     * @Param: [appCode, backUpFilePath] <br>
     * @Return: cn.afterturn.easypoi.excel.entity.result.ExcelImportResult<com.hex.ds.hdrs.cron.dto.CronTaskImportDto> <br>
     * @Description：读取Excel中数据并做校验<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 13:34 <br>
     * @Version： V2.0.2<br>
     */
    private ExcelImportResult<CronTaskImportDto> readExcel(String appCode, String backUpFilePath) {
        ImportParams importParams = new ImportParams();
        importParams.setNeedVerify(true);//开启校验
        importParams.setVerifyHandler(cronTaskImportVerifyHandler); //自定义校验
        cronTaskImportVerifyHandler.setAppCode(appCode);
        cronTaskImportVerifyHandler.getThreadLocal().remove();
        return ExcelImportUtil.importExcelMore(new File(backUpFilePath), CronTaskImportDto.class, importParams);
    }

    /*
     * @Method: updateDbDataByExcel <br>
     * @Param: [excelData, batchTag] <br>
     * @Return: void <br>
     * @Description：更新Excel中数据<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/3 13:35 <br>
     * @Version： V2.0.2<br>
     */
    private void updateDbDataByExcel(List<CronTaskImportDto> excelData, String batchTag, String appCode) {
        for (CronTaskImportDto importDto : excelData) {
            CronTaskDefDto cronTaskDefDto = converter.toCronTaskDefDto(importDto, batchTag, appCode);
            CronTaskDef cronTaskDef = queryByTaskCode(importDto.getTaskCode());
            if (ExcelImportOperationEnum.A.getName().equals(importDto.getOperation())) {
                add(cronTaskDefDto);
            } else if (ExcelImportOperationEnum.U.getName().equals(importDto.getOperation()) && cronTaskDef != null) {
                cronTaskDefDto.setPkId(cronTaskDef.getPkId());
                modifyById(cronTaskDefDto);
            } else if (ExcelImportOperationEnum.D.getName().equals(importDto.getOperation()) && cronTaskDef != null) {
                removeById(cronTaskDef.getPkId());
            }
        }
    }

    /*
     * @Method: exportExcel <br>
     * @Param: [cronTaskDefDto] <br>
     * @Return: java.lang.String <br>
     * @Description：导出定时任务定义<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 10:26 <br>
     * @Version： V2.0.2<br>
     */
    public String exportExcel(CronTaskDefDto cronTaskDefDto){
        List<CronTaskDef> exportDtoList = queryList(cronTaskDefDto);
        String backUpPath = createSavePath();
        String templateFilePath = "static/cron_task_import_template.xlsx";
        String saveFilePath = backUpPath + File.separator + "定时任务定义数据.xlsx";
        ExcelDownUtil.generatedExcel(converter.toCronTaskExportDtoList(exportDtoList), templateFilePath, saveFilePath);
        return saveFilePath;
    }

    /*
     * @Method: createSavePath <br>
     * @Param: [] <br>
     * @Return: java.lang.String <br>
     * @Description：创建导出Excel备份目录<br>
     * @Author： wz.li<br>
     * @Date： 2023/4/4 10:25 <br>
     * @Version： V2.0.2<br>
     */
    private String createSavePath() {
        String savePath = filePath + File.separator + "static/downExcel/cronTask" + File.separator + cn.hutool.core.date.DateUtil.today() + File.separator + DateUtil.current(false);
        cn.hutool.core.io.FileUtil.mkdir(savePath);
        return FilenameUtils.separatorsToSystem(savePath);
    }
}
